#include <cmath>
#include <optional>
#include <SFML/Graphics/CircleShape.hpp>
#include <SFML/Graphics/RenderTarget.hpp>
#include <SFML/Graphics/VertexArray.hpp>
#include <nice2d/util.h>
#include <nice2d/charts/line.h>

class LineData
{
public:
    LineData()
    {
        vertices.setPrimitiveType(sf::LineStrip);
    }

    void addPoint(const sf::Vector2f &point)
    {
        sf::CircleShape circle;
        circle.setRadius(pointRadius);
        circle.setFillColor(pointColor);
        circle.setPosition(point - sf::Vector2f(pointRadius /** sqrtf(2)*/, pointRadius /** sqrtf(2)*/));
        points.push_back(circle);
    }

    sf::VertexArray vertices;
    std::optional<sf::Vector2f> previous;
    float width = 1.0f;
    sf::Color color;

    bool showPoint = false;
    sf::Color pointColor = sf::Color(255, 0, 150);
    float pointRadius = 3.0f;
    std::vector<sf::CircleShape> points;
};

Line::Line():
    data(new LineData)
{
}

Line::~Line()
{
}

void Line::setPointOption(bool show, const sf::Color &color, float radius)
{
    data->showPoint = show;
    data->pointColor = color;
    data->pointRadius = radius;
}

void Line::clear()
{
    data->vertices.clear();
    data->points.clear();
    data->previous = sf::Vector2f(-1, -1);
}

size_t Line::getSize() const
{
    return data->vertices.getVertexCount();
}

void Line::onDrawNode(sf::RenderTarget &target, sf::RenderStates states) const
{
    target.draw(data->vertices, states);

    if (data->showPoint) {
        std::for_each(data->points.begin(), data->points.end(), [&](const sf::CircleShape & circle) {
            target.draw(circle, states);
        });
    }
}

void Line::add(const sf::Vector2f &point,
               float thickness,
               const sf::Color &color)
{
    data->addPoint(point);

    if (data->previous.has_value()) {
        sf::Vector2f p1 = data->previous.value();
        sf::Vector2f p2 = point;
        sf::Vector2f v = p2 - p1;

        v /= length(v);
        sf::Vector2f vp(-v.y, v.x);

        sf::Vector2f temp;
        temp.x = p1.x + data->width / 2 * vp.x;
        temp.y = p1.y + data->width / 2 * vp.y;
        data->vertices.append(sf::Vertex(temp, color));

        temp.x = p1.x - data->width / 2 * vp.x;
        temp.y = p1.y - data->width / 2 * vp.y;
        data->vertices.append(sf::Vertex(temp, color));

        temp.x = p2.x + thickness / 2 * vp.x;
        temp.y = p2.y + thickness / 2 * vp.y;
        data->vertices.append(sf::Vertex(temp, color));

        temp.x = p2.x - thickness / 2 * vp.x;
        temp.y = p2.y - thickness / 2 * vp.y;
        data->vertices.append(sf::Vertex(temp, color));
    }

    data->previous = point;
    data->width = thickness;
    data->color = color;
}